import EventType from "../../module/common/EventType";
import UI_mask from "../../moduleui/common/UI_mask";
import NetMask from "../../moduleview/common/NetMask";
import { SpineManage } from "../anim/SpineManage";
import BadgeManage from "../badge/BadgeManage";
import { IModule } from "../common/Common";
import UIConfig from "../config/UIConfig";
import Context from "../context/Context";
import EventManage from "../event/EventManage";
import { Logger } from "../log/logger";
import Pool from "../pool/Pool";
import TimeManage from "../time/TimerManage";
import Utils from "../utils/Utils";
import { UIBase, UIItemConstructor } from "./UIBase";
import { openAni } from "./UIConst";
import { UIOption, UIPackage, Fview, View, UILayer, UIInfo, Bit, GObjectPool, FImage } from "./UIDefine";
import UIRes from "./UIRes";


export default class UIManage implements IModule {

    /**ui包映射 */
    private _pkgList = new Map<number, string>();

    /**打开列表 */
    private _openList = new Map<number, UIInfo>();

    /**打开队列 */
    private _openQueue: { c: UIItemConstructor<Fview>, opt?: Partial<UIOption> }[] = [];

    /**缓存列表 */
    private _tempList = new Map<number, UIInfo>();

    /**层级管理  */
    private _layer: { [k: number]: Fview } = Object.create(Object);

    /**UI清理时间 */
    private _clearUITime: number;

    /**UI资源清理时间 */
    private _clearUIResTime: number;

    /**当前置顶UI */
    private _topInstance: UIBase<any>;

    private evm: EventManage;


    initialize(): void {
        this.evm = Context.Instance.get(EventManage);
        let time = Context.Instance.get(TimeManage).second;
        this._clearUITime = time + UIConfig.ViewClear;
        this._clearUIResTime = time + UIConfig.ResourcesClear;

        // fgui.UIConfig.windowModalWaiting = UI_mask.URL;
    }

    onTimeLoop(): void {
        let time = Context.Instance.get(TimeManage).second;

        if (time >= this._clearUITime) {
            this._clearUITime = time + UIConfig.ViewClear;
            this.clearCache();
        }

        if (time >= this._clearUIResTime) {
            this._clearUIResTime = time + UIConfig.ResourcesClear;
            UIRes.I.clear();
        }

        this._openList.forEach((v, k) => {
            this.applyF(v.instance, "onLoopSecond");
        })
    }

    onTimeLoopFrame(): void {
        this._openList.forEach((v, k) => this.applyF(v.instance, "onLoop"));
    }


    open<T extends Fview>(view: UIItemConstructor<T>, opt?: Partial<UIOption>) {

        if (!Context.Instance.has(view)) {
            Logger.logView(view.name, "UI未注册");
            return;
        }

        let pkg = this.getDependUI(view);
        let data = null, layer = UILayer.dialog, showLogin = 0, bgMask = false, clickMask = false, handler: Laya.Handler = null, permanent = false, overwrite = false;

        if (opt) {
            !Utils.isNil(opt.dependencePackage) && this.normallizeDepend(opt.dependencePackage, pkg)
            !Utils.isNil(opt.data) && (data = opt.data);
            !Utils.isNil(opt.layer) && (layer = opt.layer);
            !Utils.isNil(opt.loadView) && (showLogin = 1);
            !Utils.isNil(opt.bgMask) && (bgMask = opt.bgMask);
            !Utils.isNil(opt.clickMask) && (clickMask = opt.clickMask);
            !Utils.isNil(opt.owerwrite) && (overwrite = opt.owerwrite);
            !Utils.isNil(opt.openHandler) && (handler = opt.openHandler);
            !Utils.isNil(opt.permanent) && (permanent = opt.permanent);
        }

        let id = Utils.getGUIDByObject(view);
        let resMgr = UIRes.I;

        if (this.hasOpen(id)) {

            if (!overwrite) {
                Logger.logView(view.name, "view opened");
                return;
            }

            this.closeUI(view, "overwrite");
        }

        let info = this.getIdleView(id);
        info.key = id;
        info.layer = layer;
        info.closetime = -1;
        info.permanent = permanent;
        info.opentime = Context.Instance.get(TimeManage).ms;

        if (bgMask && !info.viewMask) {
            let mask = resMgr.getObject(UI_mask);
            mask.name = "ui_mask";
            mask.setSize(Laya.stage.width, Laya.stage.height);
            info.viewMask = mask;

            if (clickMask) {
                mask.onClick(this, this.clickMaskAndClose, [view]);
            }

        }

        if (showLogin > 0 && !resMgr.hasPkg(pkg)) {
            if (id !== Utils.getGUIDByObject(NetMask)) {
                showLogin = 3;
                this.showMask();
            }
        }

        resMgr.loadByPromise(pkg).then(() => {

            Logger.logView(view.name, "打开页面");

            let v = info.instance;
            if (!v) {
                v = info.instance = Context.Instance.get(view);
                v.contex = Context.Instance;
                v.evm = Context.Instance.get(EventManage);
                v.badge = Context.Instance.get(BadgeManage);
                v.spine = Context.Instance.get(SpineManage);
                v.__uuid__ = id;
                v.ui = this;
            }

            v.data = data;

            this.addOpenView(id, info);
            this.addChild(layer, info);
            this.applyF(v, "__open", data);
            this.evm.event(EventType.ON_OPENUI, id);
            handler && handler.runWith(info.instance);
            showLogin === 3 && this.hideMask();

            this.onTopUI();
        })

    }

    /**
     * 在队列中打开
     * @param c 
     * @param data 携带数据
     */
    openQueue<T extends Fview>(c: UIItemConstructor<T>, opt?: Partial<UIOption>) {
        let queue = this._openQueue;
        queue.push({ c: c, opt: opt });

        if (queue.length === 1) {
            return this.open(c, opt);
        }
    }

    clearQueue() {
        this._openQueue = [];
    }


    /**
     * 带缩放动画的弹出
     * @param c 
     * @param data 打开携带数据
     * @param ani 弹出动画
     */
    popup<T extends Fview>(c: UIItemConstructor<T>, data?: any) {

        this.open(c, {
            bgMask: true,
            data: data,
            clickMask: true,
            layer: UILayer.dialog,
            openHandler: openAni
        })
    }

    popQueue<T extends Fview>(c: UIItemConstructor<T>, data?: any) {

        this.openQueue(c, {
            bgMask: true,
            data: data,
            clickMask: true,
            layer: UILayer.dialog,
            openHandler: openAni
        })
    }

    /**
     * 关闭ui，非单例页面顺序关闭
     * @param class_ui 
     * @param data 关闭携带参数，会传给UI
     */
    closeUI<T extends Fview>(class_ui: UIItemConstructor<T> | number, data: any = null) {
        let id = typeof (class_ui) === "number" ? class_ui : Utils.getGUIDByObject(class_ui);

        let viewInfo = this.getOpeneuiInfo(id);

        if (Utils.isNil(viewInfo) || Utils.isNil(viewInfo.instance)) {
            Logger.logView("view closed", class_ui["name"]);
            return;
        }

        viewInfo.layer = -1;
        viewInfo.opentime = -1;
        viewInfo.closetime = Context.Instance.get(TimeManage).ms;

        this.evm.event(EventType.ON_CLOSEUI, id);
        this.applyF(viewInfo.instance, "__close", data);
        this.closeInstance(id, viewInfo);

        this.onTopUI();
    }

    /**
     * 显示全局遮罩
     * @param mask 背景半透明
     * @param dely 延迟显示
     * @param autoClose 自动关闭
     * @returns 
     */
    showMask(mask: boolean = false, dely: number = 200, autoClose: number = 2000) {

        let viewInfo = this.getOpeneuiInfo(NetMask);
        if (viewInfo && viewInfo.instance) {

            let view = viewInfo.instance;
            view.updateData(mask, dely, autoClose);
            return;
        }

        let data = Object.create(null);
        data.dely = dely;
        data.mask = mask;
        data.autoClose = autoClose;

        this.open(NetMask, {
            layer: UILayer.top,
            data: data,
            permanent: true,
            bgMask: false
        });
    }

    hideMask() {
        if (!this.hasOpen(NetMask)) {
            return;
        }

        this.closeUI(NetMask, "hideMask")
    }

    /**
     * 获取实列的页面
     * @param class_ui 
     * @returns 
     */
    getOpeneuiInfo<T extends Fview>(class_ui: UIItemConstructor<T> | number) {
        let id = typeof (class_ui) === "number" ? class_ui : Utils.getGUIDByObject(class_ui);
        let viewInfo = this._openList.get(id);
        if (Utils.isNil(viewInfo) || Utils.isNil(viewInfo.instance)) {
            return void 0;
        }
        return viewInfo;
    }

    getOpenUI<T extends Fview>(id: UIItemConstructor<T> | number): UIBase<T> {
        let info = this.getOpeneuiInfo(id);
        if (info) {
            return info.instance;
        }

        return null;
    }

    /**
     * view是否已经打开
     * @param class_ui 
     */
    hasOpen<T extends Fview>(class_ui: UIItemConstructor<T> | number) {
        return !!this.getOpeneuiInfo(class_ui);
    }

    getLayer(num: UILayer) {
        if (num < 1 || num > 5) return;
        let l0 = this._layer[num];
        if (Utils.isNil(l0)) {
            l0 = this._layer[num] = new View();
            l0.name = "L" + num;
            // l0.displayObject.zOrder = num;
            l0.sortingOrder = num;
            l0.setSize(Laya.stage.width, Laya.stage.height);
            Laya.stage.addChild(l0.displayObject);
        }

        return l0;
    }

    /**注册ui */
    regUI(pkg: string, key: number) {
        this._pkgList.set(key, pkg);
    }


    clearCache() {

        let tempList = this._tempList;
        if (tempList.size <= 0) {
            return;
        }

        Logger.logView(this._tempList, "清理页面缓存");
        let res = UIRes.I;

        tempList.forEach((v, k) => {
            if (v.permanent) {
                return;
            }

            let view = v.instance;
            if (view) {
                this.applyF(view, "__dispose");
                let pkgName = this._pkgList.get(v.key);
                res.removeRefrence(pkgName);
            }
            if (v.viewMask) {
                res.recoverObject(v.viewMask);
            }

            v.clear();
        })
        tempList.clear();
    }

    get topView() {
        return this._topInstance;
    }


    private onTopUI() {
        let target: UIInfo;
        this._openList.forEach((v => {
            if (!target) {
                target = v;
            } else if (v.layer > target.layer) {
                target = v;
            } else if (v.opentime > target.opentime) {
                target = v;
            }
        }))

        if (target && target.instance) {
            if (this._topInstance && this._topInstance === target.instance) {
                return;
            }

            Utils.apply(target.instance, "onTop");
            Logger.logView(target.instance.constructor.name, "UI置顶");
        }
    }


    /**获取View依赖包 */
    private getDependUI<T extends Fview>(c: new () => UIBase<T>) {
        let pkgs = this._pkgList, key = Utils.getGUIDByObject(c);
        if (pkgs.has(key)) {
            return [pkgs.get(key)]
        }
        return [];
    }

    private normallizeDepend(pkg: string | string[], out: string[]) {
        if (!pkg) return;

        if (typeof pkg === "string") {
            out.push(pkg);
            return;
        }

        out.push(...pkg);
    }


    private closeInstance(id: number, info: UIInfo) {
        let inst = info.instance;
        let mask = info.viewMask;
        let res = UIRes.I;

        this._openList.delete(id);

        if (inst.view && inst.view.parent) {
            inst.view.parent.removeChild(inst.view);
        }

        if (mask) {
            mask.parent && mask.parent.removeChild(mask);
            res.recoverObject(mask);
            info.viewMask = null;
        }

        this._tempList.set(id, info);

        let queueView = this._openQueue[0];
        if (queueView && Utils.getGUIDByObject(queueView.c) === id) {
            this._openQueue.shift();
            queueView = this._openQueue[0];
            queueView && this.open(queueView.c, queueView.opt);
        }

    }

    private addOpenView(id: number, v: UIInfo) {
        let openArray = this._openList;
        openArray.set(id, v);
    }

    private applyF(target: any, fName: string, ...args: any[]) {
        if (!Reflect.has(target, fName)) {
            return;
        }
        return target[fName](...args);
    }

    private getIdleView(key: number) {
        let info = this._tempList.get(key);
        if (Utils.isNil(info)) {
            return Pool.get(UIInfo);
        }
        this._tempList.delete(key);
        return info;
    }

    private addChild(l: number, info: UIInfo) {
        let layer = this.getLayer(l);
        let mask = info.viewMask;
        let view = info.instance.view as Fview;
        if (!mask) {
            layer.addChild(view);
            view.center(true);
            return;
        }
        // let tip = <FImage>mask["m_tip"];
        // tip && Tween.once(tip).set({ alpha: 0 }).to({ alpha: 1 }, 200);

        mask.addChild(view);
        layer.addChild(mask);
        view.center();
    }

    private clickMaskAndClose(c: any, e: Laya.Event) {
        e.stopPropagation();
        this.closeUI(c, "empty");
    }


}